package com.haozi.ehub.user.config;

import com.haozi.ehub.user.autoconfig.PassProperties;
import com.haozi.ehub.core.handler.*;
import com.haozi.ehub.core.security.SecurityPasswordEncoder;
import com.haozi.ehub.core.security.UserDetailsServiceImpl;
import com.haozi.ehub.core.service.AdminInfoService;
import com.haozi.ehub.core.service.UserInfoService;
import com.haozi.ehub.core.util.CodeUtils;
import com.haozi.ehub.core.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;

/**
 * @author ：terry
 * @date ：Created in 2020/6/12 16:07
 * @version: 1.0
 */
@Slf4j
@Configuration
@EnableWebSecurity
@EnableAutoConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@SuppressWarnings(value = "all")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    RedisUtils redisUtil;
    @Autowired
    UserInfoService userInfoService;
    @Autowired
    AdminInfoService adminInfoService;
    @Autowired
    private PassProperties passProperties;

    /**
     * Security相关配置
     * @author     ： terry
     * @date       ： Created in 2020/6/28 9:01
     * @return httpSecurity
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        List<String> passUriList = passProperties.getUrl().getPassUri();
        String loginPath = passProperties.getElement().getLoginUrl();
        String[] path = passUriList.toArray(new String[passUriList.size()]);
        //配置csrf
        httpSecurity.csrf().disable();
        //开启跨域共享
        httpSecurity.cors().disable();
        //设置自定义token验证
        httpSecurity.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        //跨域放行(不知有没有效果)
        httpSecurity.authorizeRequests().antMatchers(HttpMethod.OPTIONS,CodeUtils.URL_ALL).permitAll();
        //跨域放行(不知有没有效果)
        httpSecurity.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
        //设置过滤掉的路径
        httpSecurity.authorizeRequests().antMatchers(path).permitAll();
        //httpSecurity.authorizeRequests().antMatchers(loginKeyPath).permitAll();
        //登录后可以访问任何请求
        httpSecurity.authorizeRequests().anyRequest().authenticated();
        //设置登陆路径
        httpSecurity.formLogin().loginProcessingUrl(loginPath);
        //设置登陆时名称参数
        httpSecurity.formLogin().usernameParameter(CodeUtils.USER_NAME);
        //设置登陆时密码参数
        httpSecurity.formLogin().passwordParameter(CodeUtils.PASSWORD);
        //配置登陆验证成功
        httpSecurity.formLogin().successHandler(authenticationSuccessHandler()).permitAll();
        //配置登陆验证失败
        httpSecurity.formLogin().failureHandler(authenticationFailureHandler()).permitAll();
        //注销请求可以任何时候访问
        httpSecurity.logout().permitAll();
        //配置退出登陆认证
        httpSecurity.logout().logoutSuccessHandler(logoutSuccessHandler()).permitAll();
        //配置未登录认证
        //httpSecurity.httpBasic().authenticationEntryPoint(entryPointHandler);
        httpSecurity.exceptionHandling().authenticationEntryPoint(authenticationEntryPointHandler());
        //未登录请求资源返回403
        httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
        //关闭基于session的权限管理
        httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    /**
     * 角色继承
     * @author     ： terry
     * @date       ： Created in 2020/6/28 9:01
     * @return： RoleHierarchy
     */
    @Bean
    public RoleHierarchy roleHierarchy(){
        RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
        String separator = System.lineSeparator();
        //角色继承
        StringBuilder roleInherit = new StringBuilder();
        roleInherit.append("ROLE_sup_admin > ROLE_admin");
        roleInherit.append(separator);
        roleInherit.append("ROLE_admin > ROLE_sup_user");
        roleInherit.append(separator);
        roleInherit.append("ROLE_sup_user > ROLE_user");
        roleHierarchyImpl.setHierarchy(roleInherit.toString());
        return roleHierarchyImpl;
    }

    /**
     * 用户验证实现
     * @author     ： terry
     * @date       ： Created in 2020/6/28 9:01
     * @return UserDetailsService
     */
    @Bean
    @Override
    public UserDetailsService userDetailsService(){
        return new UserDetailsServiceImpl(redisUtil,userInfoService,adminInfoService);
    }

    /**
     * 用户验证
     * @author     ： terry
     * @date       ： Created in 2020/6/28 9:00
     * @param auth
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
    }

    /**
     * 密码加密配置
     * @author     ： terry
     * @date       ： Created in 2020/6/28 9:00
     * @return PasswordEncoder
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new SecurityPasswordEncoder();
    }


    /**
     * 登陆成功
     * @author     ： terry
     * @date       ： Created in 2020/6/24 16:00
     * @return： SecurityAuthenticationSuccessHandler
     */
    @Bean
    public SecurityAuthenticationSuccessHandler authenticationSuccessHandler(){
        return new SecurityAuthenticationSuccessHandler();
    }

    /**
     * 登陆失败
     * @author     ： terry
     * @date       ： Created in 2020/6/24 16:00
     * @return： SecurityAuthenticationFailureHandler
     */
    @Bean
    public SecurityAuthenticationFailureHandler authenticationFailureHandler(){
        return new SecurityAuthenticationFailureHandler();
    }

    /**
     * 成功注销
     * @author     ： terry
     * @date       ： Created in 2020/6/24 16:00
     * @return： SecurityLogoutSuccessHandler
     */
    @Bean
    public SecurityLogoutSuccessHandler logoutSuccessHandler(){
        return new SecurityLogoutSuccessHandler();
    }

    /**
     * 权限验证失败
     * @author     ： terry
     * @date       ： Created in 2020/6/24 16:00
     * @return： SecurityAuthenticationEntryPointHandler
     */
    @Bean
    public SecurityAuthenticationEntryPointHandler authenticationEntryPointHandler(){
        return new SecurityAuthenticationEntryPointHandler();
    }

    /**
     * 未授权
     * @author     ： terry
     * @date       ： Created in 2020/6/24 16:00
     * @return： SecurityAccessDeniedHandler
     */
    @Bean
    public SecurityAccessDeniedHandler accessDeniedHandler(){
        return new SecurityAccessDeniedHandler();
    }

    /**
     * token验证bean配置
     * @author     ： terry
     * @date       ： Created in 2020/7/8 8:51
     * @return SecurityTokenAuthenticationFilter
     */
    @Bean
    public SecurityTokenAuthenticationFilter tokenAuthenticationFilter(){
        return new SecurityTokenAuthenticationFilter();
    }

    /**
     * 跨域配置
     * @author     ： terry
     * @date       ： Created in 2020/6/28 8:59
     * @return WebMvcConfigurer 跨域配置
     */
    @Bean
    public WebMvcConfigurer corsConfigurer(){
        log.info("Into WebMvcConfig class corsConfigurer method");
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                List<String> list = new ArrayList<>();
                list.add(CodeUtils.GET_U);
                list.add(CodeUtils.PUT_U);
                list.add(CodeUtils.POST_U);
                list.add(CodeUtils.DELETE_U);
                list.add(CodeUtils.OPTIONS_U);
                final String[] methods = list.toArray(new String[list.size()]);
                registry.addMapping(CodeUtils.URL_ALL).allowedMethods(methods);
            }
        };
    }
}
